/* DefaultDatatypeManager.java
 *
 * Copyright 2006, Tim Dwelle.
 *
 * Licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.  See
 * the License for the specific language governing
 * permissions and limitations under the License.
 *
 */

package dowry.impl;

import java.util.*;
import dowry.*;

/**
 * DefaultDatatypeManager provides a basic implementation
 * of DatatypeManager.
 *
 */
public class DefaultDatatypeManager implements DatatypeManager
{
    private Set unmappedDatatypes = new HashSet();
    private Map datatypes         = new HashMap();

    /**
     * Registers a datatype.  This datatype will not be
     * associated to any specific Java class, and thus
     * will not be returned via <code>getDatatype()</code>.
     * However, it will be available via
     * <code>getDatatypes()</code>, and possibly via
     * <code>getDatatypeByType()</code>.
     *
     * @param  datatype  the datatype to be registered
     *
     */
    public void addDatatype(Datatype datatype)
    {
        if (datatype != null)
        {
            unmappedDatatypes.add(datatype);
        }
    }

    /**
     * Register a datatype to handle the specified class.
     * This datatype will be used to handle any object that
     * is an instance of this class, or any objects
     * that extends or implements the specified class
     * (such that the closest match wins).
     *
     * @param  c         the class to register this datatype
     *                   to
     *
     * @param  datatype  the datatype to be registered
     *
     */
    public void addDatatype(Class c, Datatype datatype)
    {
        if (c != null && datatype != null)
        {
            datatypes.put(c, datatype);
        }
    }

    /**
     * Returns the classes that have been registered to
     * a datatype.
     *
     * @return  a set of Class objects registered to a
     *          datatype
     *
     */
    public Set getClasses()
    {
        return datatypes.keySet();
    }

    /**
     * Get the datatype registered to handle the specified
     * class.  If no match is found, null is returned.
     *
     * @param  c   the class to get a datatype for
     *
     * @return     the datatype registered to this class
     *
     */
    public Datatype getDatatype(Class c)
    {
        Datatype datatype = null;

        // we'll traverse the class hierarchy
        // until we find a datatype or run out
        // of superclasses
        while (datatype == null && c != null)
        {
            // lookup the datatype based on the
            // interface/class
            Class[] interfaces = c.getInterfaces();
            for (int i=0; i <= interfaces.length; i++)
            {
                Class datatypeClass = null;
                if (i == interfaces.length)
                {
                    datatypeClass = c;
                }
                else
                {
                    datatypeClass = interfaces[i];
                }

                datatype = (Datatype) datatypes.get(datatypeClass);
                if (datatype != null)
                {
                    break;
                }
            }

            // walk up the class hierarchy
            c = c.getSuperclass();
        }

        return datatype;
    }

    /**
     * Get the datatype registered to handle the specified
     * Dowry type.  If no match is found, null is returned.
     *
     * @param  type  the string Dowry type to get a datatype
     *               for
     *
     * @return       the datatype registered to this type
     *
     */
    public Datatype getDatatypeByType(String type)
    {
        Datatype datatype = null;

        Set datatypes = getDatatypes();
        Iterator datatypeIter = datatypes.iterator();
        while (datatypeIter.hasNext())
        {
            Datatype dt = (Datatype) datatypeIter.next();
            if (dt.isType(type))
            {
                datatype = dt;
                break;
            }
        }

        return datatype;
    }

    /**
     * Returns the datatype instances that are registered.
     *
     * @return  a Set of Datatype objects registered
     *
     */
    public Set getDatatypes()
    {
        Set s = new HashSet(datatypes.values());
        s.addAll(unmappedDatatypes);
        return s;
    }
}